package com.davfx.restream;

import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import com.davfx.httpnio.Http;
import com.davfx.httpnio.HttpClient;
import com.davfx.httpnio.HttpRequest;
import com.davfx.httpnio.HttpServer;
import com.davfx.httpnio.UnthreadedWorker;
import com.davfx.httpnio.HttpServer.HttpPostHandler;
import com.davfx.httpnio.HttpServer.Response;
import com.davfx.util.Log;

public final class RestreamServer implements Runnable {
	public static void main(String[] args) throws Exception {
//		DatabaseReader r = new DatabaseProvider().get("/audio2.radioreference.com/80/ca_lapd1").getReader(1270242407984L);
//		r.getHttpResponse();
//		while (r.next()) {
//			System.out.println(r.getTime());
//			r.getBuffer();
//		}
//		if (true)
//			return;
		new RestreamServer(8080).run();
	}
	
	private static final long HEARTBEAT_STEP = 1000;
	
	private final UnthreadedWorker worker;
	private final HttpClient client;
	private final Map<String, Inbound> inbounds = new HashMap<String, Inbound>();
	private final Set<Runnable> players = new LinkedHashSet<Runnable>();
	private final DatabaseProvider databaseProvider = new DatabaseProvider();
	
	public RestreamServer(int port) throws IOException {
		worker = new UnthreadedWorker(HEARTBEAT_STEP, new Runnable() {
			@Override
			public void run() {
				for (Runnable r : players) {
					r.run();
				}
			}
		});
		client = new HttpClient(worker);
		new HttpServer(worker, port, new HttpServer.HttpRequestHandler() {
			@Override
			public HttpPostHandler handle(final HttpRequest request) {
				return new HttpServer.HttpPostRequestHandler(new HttpServer.HttpBasicHandler() {
					private Player player = null;
					private Inbound inbound = null;
					private String id;
					private Response response;
					@Override
					public void write(Map<String, String> postParameters, Response response) {
						String host;
						String path;
						int port;
						long from = -1;
						try {
							id = request.getPath();
							Log.log("Connection id: " + id);
							if (id.charAt(0) != Http.PATH_SEPARATOR) {
								throw new IOException("Invalid request: " + id);
							}
							int u = id.indexOf(Http.PATH_SEPARATOR, 1);
							int v = id.indexOf(Http.PATH_SEPARATOR, u + 1);
							if ((u == -1) || (v == -1) || (v < u)) {
								throw new IOException("Invalid request: " + id);
							}
							host = id.substring(1, u);
							String portValue = id.substring(u + 1, v);
							try {
								port = Integer.parseInt(portValue);
							} catch (NumberFormatException nfe) {
								throw new IOException("Invalid port: " + portValue);
							}
							path = id.substring(v);
							
							String fromValue = request.getParameters().get("from");
							if (fromValue != null) {
								try {
									from = Long.parseLong(fromValue);
								} catch (NumberFormatException nfe) {
									throw new IOException("Invalid from: " + fromValue);
								}
							}
						} catch (IOException e) {
							Log.error("Client error", e);
							response.close();
							return;
						}

						Database database = databaseProvider.get(id);
						
						if (from >= 0) {
							player = new Player(new Runnable() {
								@Override
								public void run() {
									players.remove(player);
								}
							}, database, from, response);
							players.add(player);
							return;
						}
						
						this.response = response;
						inbound = inbounds.get(id);
						if (inbound == null) {
							inbound = new Inbound(new Runnable() {
								@Override
								public void run() {
									inbounds.remove(id);
								}
							}, database);
							inbounds.put(id, inbound);
							Log.log("[" + inbounds.size() + "] Connecting to: " + host + ":" + port + path);
							HttpClient.Connection connection = client.send(new HttpRequest(host, port, HttpRequest.Method.GET, path), inbound);
							inbound.set(connection);
						}
						
						Log.log("[" + inbounds.size() + "] Adding client");
						inbound.add(response);
					}
					@Override
					public void closed() {
						if (player != null) {
							player.closed();
							players.remove(player);
						}
						if (inbound != null) {
							inbound.remove(response);
							Log.log("[" + inbounds.size() + "] Disconnect");
						}
					}
				});
			}
			@Override
			public void failed(IOException e) {
				Log.error("Server error", e);
			}
		});
	}

	@Override
	public void run() {
		worker.run();
	}
}
